// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DBConfigurationtHelpers.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.DataModel
{
    #region Usings

    using System;
    using System.IO;
    using System.Linq;
    using System.Text;

    using Org.OpenTrader.Framework.LiveObjects;
    using Org.OpenTrader.Framework.LiveObjects.Interfaces;

    #endregion

    /// <summary>
    /// The db configuration helpers.
    /// </summary>
    public class DBConfigurationHelpers
    {
        #region Public Methods

        /// <summary>
        /// The delete db configuration.
        /// </summary>
        /// <param name="g">
        /// The g.
        /// </param>
        public static void DeleteDBConfiguration(Guid g)
        {
            var dbConfigurations = from c in Singleton<DBFramework>.Instance.Entities.DBConfiguration where c.ID == g select c;

            if (dbConfigurations.Count() == 1)
            {
                var dbConfiguration = dbConfigurations.First();

                // Delete
                Singleton<DBFramework>.Instance.Entities.DeleteObject(dbConfiguration);

                // Save Changes
                var n = Singleton<DBFramework>.Instance.Commit();
            }
        }

        /// <summary>
        /// The load configuration.
        /// </summary>
        /// <param name="configurationId">
        /// The configurationId.
        /// </param>
        /// <param name="className">
        /// The className.
        /// </param>
        /// <param name="container">
        /// The container.
        /// </param>
        /// <returns>
        /// </returns>
        public static IConfiguration LoadConfiguration(Guid configurationId, string className, ILiveObjectContainer container)
        {
            return LoadConfiguration(configurationId, className, ConfigurationFieldAttribute.DefaultSchema, container);
        }

        /// <summary>
        /// The load configuration.
        /// </summary>
        /// <param name="configurationId">
        /// The configurationId.
        /// </param>
        /// <param name="className">
        /// The className.
        /// </param>
        /// <param name="configurationSchemaName">
        /// The configurationSchemaName.
        /// </param>
        /// <param name="container">
        /// The container.
        /// </param>
        /// <returns>
        /// </returns>
        public static IConfiguration LoadConfiguration(Guid configurationId, string className, string configurationSchemaName, ILiveObjectContainer container)
        {
            // Get a Schema
            var configurationSchema = container.GetConfigurationSchema(className, configurationSchemaName);

            // No schema, no configuration !
            if (configurationSchema == null)
            {
                return null;
            }

            // Setup a new configuration this the given schema. All Fields are populated using the right ConfigurationField.xyz
            IConfiguration configuration = new Configuration(configurationId, configurationSchema, container);

            var dbConfigurations = from c in Singleton<DBFramework>.Instance.Entities.DBConfiguration where c.ID == configurationId select c;

            if (dbConfigurations.Count() == 1)
            {
                var dbConfiguration = dbConfigurations.First();

                dbConfiguration.DBConfigurationField.Load();
                foreach (var dbConfigurationField in dbConfiguration.DBConfigurationField)
                {
                    // Ignore fields present in DB that are not part of the configuration schema
                    if (!configurationSchema.Fields.Contains(dbConfigurationField.Name))
                    {
                        continue;
                    }

                    // The string representation of the field content
                    var mm = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(dbConfigurationField.Data));
                    var sr = new StreamReader(mm);

                    // Binary contents (if any) are vehiculated inside SerializationContext
                    var context = new SerializationContext();
                    dbConfigurationField.DBConfigurationFieldBinaryContents.Load();
                    foreach (var dbConfigurationFieldBinaryContent in dbConfigurationField.DBConfigurationFieldBinaryContents)
                    {
                        context.BinaryContents[dbConfigurationFieldBinaryContent.Name] = dbConfigurationFieldBinaryContent.BinaryData;
                    }

                    // Let's deserialize (supports Constants.NotSet and son on)
                    var status = configuration[dbConfigurationField.Name].DeSerialize(sr, context);
                    if (status)
                    {
                        var configurationField = configuration[dbConfigurationField.Name];
                        if (configurationField is BinaryConfigurationField)
                        {
                            (configurationField as BinaryConfigurationField).Name = dbConfigurationField.Data;
                        }
                    }
                }
            }

            return configuration;
        }

        /// <summary>
        /// The save configuration.
        /// </summary>
        /// <param name="configuration">
        /// The configuration.
        /// </param>
        /// <returns>
        /// The save configuration.
        /// </returns>
        public static bool SaveConfiguration(IConfiguration configuration)
        {
            if (configuration == null)
            {
                return false;
            }

            DBConfiguration dbConfiguration;

            var dbConfigurations = from c in Singleton<DBFramework>.Instance.Entities.DBConfiguration where c.ID == configuration.ID select c;

            // New one
            if (dbConfigurations.Count() == 0)
            {
                dbConfiguration = DBConfiguration.CreateDBConfiguration(configuration.ID);
                Singleton<DBFramework>.Instance.Entities.AddToDBConfiguration(dbConfiguration);
            }
            else
            {
                // Exisiting one
                dbConfiguration = dbConfigurations.First();
            }

            // Wanders through schema fields
            foreach (var fieldName in configuration.ConfigurationSchema.Fields)
            {
                var configurationField = configuration[fieldName];
                string data = null;

                // Serialization
                var context = new SerializationContext();
                var mm = new MemoryStream();
                var sw = new StreamWriter(mm);
                sw.AutoFlush = true;
                var status = configurationField.Serialize(sw, context);
                if (status)
                {
                    // Data is in the stream
                    data = ASCIIEncoding.ASCII.GetString(mm.GetBuffer(), 0, (int)mm.Length).TrimEnd(new[] { '\n', '\r' });
                }

                // find field
                var dbConfigurationFields = from f in dbConfiguration.DBConfigurationField where f.Name == fieldName select f;
                DBConfigurationField dbConfigurationField;
                if (dbConfigurationFields.Count() == 1)
                {
                    // Modify
                    dbConfigurationField = dbConfigurationFields.First();
                    dbConfigurationField.Data = data;
                }
                else
                {
                    // Create
                    dbConfigurationField = DBConfigurationField.CreateDBConfigurationField(Guid.NewGuid(), fieldName, data);
                    dbConfiguration.DBConfigurationField.Add(dbConfigurationField);
                }

                // Attach binary contents (if any)
                foreach (var binaryContent in context.BinaryContents)
                {
                    var dbBinaryContents = from f in dbConfigurationField.DBConfigurationFieldBinaryContents where f.Name == binaryContent.Key select f;

                    DBConfigurationFieldBinaryContents dbBinaryContent;
                    if (dbBinaryContents.Count() == 1)
                    {
                        // Modify
                        dbBinaryContent = dbBinaryContents.First();
                        dbBinaryContent.BinaryData = binaryContent.Value;
                    }
                    else
                    {
                        // Create
                        dbBinaryContent = DBConfigurationFieldBinaryContents.CreateDBConfigurationFieldBinaryContents(
                            Guid.NewGuid(), binaryContent.Key, binaryContent.Value, Timings.Now);
                        dbConfigurationField.DBConfigurationFieldBinaryContents.Add(dbBinaryContent);
                    }
                }
            }

            // Save Changes
            var n = Singleton<DBFramework>.Instance.Commit();

            return n > 0;
        }

        #endregion
    }
}